use std::collections::HashSet;

#[derive(PartialEq, Debug)]
pub struct Card {
    id: i32,
    winning_numbers: HashSet<i32>,
    numbers: HashSet<i32>,
}

impl Card {
    pub fn winning_size(&self) -> u32 {
        self.numbers
            .iter()
            .filter(|&number| self.winning_numbers.contains(number) )
            .count() as u32
    }

    pub fn points(&self) -> u32 {
        let size = self.winning_size();
        if size == 0 { 0 } else { 2u32.pow(size - 1) }
    }

    pub fn id(&self) -> i32 {
        self.id
    }
}

impl From<&str> for Card {
    fn from(value: &str) -> Self {
        let colon_pos = value.find(":").expect("Can't convert to Card");
        let line_pos = value.find("|").expect("Can't convert to Card");

        let id_str = value[5..colon_pos].trim();
        let winning_numbers_str = value[colon_pos + 1..line_pos].trim();
        let numbers_str = value[line_pos + 1..].trim();

        let id: i32 = id_str.parse().expect("Can't convert to id");

        let get_numbers = |s: &str| -> HashSet<i32> {
            s.split(" ")
             .filter(|token| { 
                let token = token.trim();
                !token.is_empty()
              })
             .map(|token| token.parse().expect("Can't convert to number."))
             .collect()
        };
        
        let winning_numbers = get_numbers(winning_numbers_str);
        let numbers = get_numbers(numbers_str);

        Self { id, winning_numbers, numbers }
    }
}

#[cfg(test)]
mod test {
    use super::Card;
    use std::collections::HashSet;

    #[test]
    fn test_parse_card() {
        let card: Card = "Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53".into();
        assert_eq!(
            card,
            Card {
                id: 1,
                winning_numbers: HashSet::<i32>::from([41, 48, 83, 86, 17]),
                numbers: HashSet::<i32>::from([83, 86, 6, 31, 17, 9, 48, 53]),
            }
        );
    }
}